package com.diamond.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigInteger;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.PropertyUtils;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.transform.Transformers;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.diamond.dao.GenericDAO;
import com.diamond.util.BeanTransformer;
import com.mysql.jdbc.log.Log;

/**
 * @author JSmart Tools
 */
public class GenericHibernateDAO<T extends Serializable, PK extends Serializable> extends HibernateDaoSupport implements GenericDAO<T, PK>
	{

	private Class<T> entityClass;

	public GenericHibernateDAO() {
		this.entityClass = null;
		Class c = getClass();
		Type t = c.getGenericSuperclass();
		if (t instanceof ParameterizedType) {
			Type[] p = ((ParameterizedType) t).getActualTypeArguments();
			this.entityClass = (Class<T>) p[0];
		}
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#bulkUpdate(java.lang.String)
	 */
	public int bulkUpdate(String queryString) {
		return getHibernateTemplate().bulkUpdate(queryString);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#bulkUpdate(java.lang.String, java.lang.Object[])
	 */
	public int bulkUpdate(String queryString, Object[] values) {
		return getHibernateTemplate().bulkUpdate(queryString, values);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#findById(PK)
	 */
	public T findById(PK id) {
		return (T) getHibernateTemplate().get(entityClass, id);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#getRowCount(java.lang.String)
	 */
	public Long getRowCount(String HQL) {
		return (Long) getHibernateTemplate().find(HQL).get(0);
	}
	
	public Integer getRowCountBySql(String SQL) {
		BigInteger count=(BigInteger) findByNHQL(SQL).get(0);
		return count.intValue();
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#getRowCount(org.hibernate.criterion.DetachedCriteria)
	 */
	public Integer getRowCount(DetachedCriteria criteria) {
		criteria.setProjection(Projections.rowCount());
		List list = this.findByCriteria(criteria, 0, 1);
		return (Integer) list.get(0);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#getStatValue(org.hibernate.criterion.DetachedCriteria, java.lang.String, java.lang.String)
	 */
	public Object getStatValue(DetachedCriteria criteria, String propertyName,
			String StatName) {
		if (StatName.toLowerCase().equals("max"))
			criteria.setProjection(Projections.max(propertyName));
		else if (StatName.toLowerCase().equals("min"))
			criteria.setProjection(Projections.min(propertyName));
		else if (StatName.toLowerCase().equals("avg"))
			criteria.setProjection(Projections.avg(propertyName));
		else if (StatName.toLowerCase().equals("sum"))
			criteria.setProjection(Projections.sum(propertyName));
		else
			return null;
		List list = this.findByCriteria(criteria, 0, 1);
		return list.get(0);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#findLikeByEntity(T, java.lang.String[])
	 */
	public List<T> findLikeByEntity(T entity, String[] propertyNames) {
		Criteria criteria = this.createCriteria();
		for (String property : propertyNames) {
			try {
				Object value = PropertyUtils.getProperty(entity, property);
				if (value instanceof String) {
					// MatchMode.ANYWHERE �൱�� %value%
					criteria.add(Restrictions.like(property, (String) value,
							MatchMode.ANYWHERE));
					criteria.addOrder(Order.asc(property));
				} else {
					criteria.add(Restrictions.eq(property, value));
					criteria.addOrder(Order.asc(property));
				}
			} catch (Exception ex) {
				// ������Ч�ļ����ο���ݡ�
			}
		}
		return (List<T>) criteria.list();
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#findCacheByHQL(java.lang.String)
	 */
	@SuppressWarnings("unchecked")
	public List findCacheByHQL(final String HQL) {
		return this.getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException {
				return session.createQuery(HQL).setCacheable(true).list();
			}
		});
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#findByHQL(java.lang.String)
	 */
	public List findByHQL(String queryString) {
		return getHibernateTemplate().find(queryString);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#findByHQL(int, int, java.lang.String)
	 */
	public List<T> findByHQL(final int currPage, final int pagesize,
			final String hql) {
		List list = this.getHibernateTemplate().executeFind(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException, SQLException {
						List result = session.createQuery(hql).setFirstResult(
								currPage > 1 ? (currPage - 1) * pagesize : 0)
								.setMaxResults(pagesize).list();
						return result;
					}
				});
		return list;
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#findByNHQL(java.lang.String)
	 */
	public List findByNHQL(final String SQL) {
		List list = this.getHibernateTemplate().executeFind(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException, SQLException {
						SQLQuery query = session.createSQLQuery(SQL);
						return query.list();
					}
				});
		return list;

	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#findByCriteria(org.hibernate.criterion.DetachedCriteria)
	 */
	public List findByCriteria(DetachedCriteria criteria) {
		return getHibernateTemplate().findByCriteria(criteria);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#findByCriteria(org.hibernate.criterion.DetachedCriteria, int, int)
	 */
	public List findByCriteria(DetachedCriteria criteria, int firstResult,int maxResults) {
		return getHibernateTemplate().findByCriteria(criteria, firstResult,
				maxResults);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#findEqualByEntity(T, java.lang.String[])
	 */
	public List<T> findEqualByEntity(T entity, String[] propertyNames) {
		Criteria criteria = this.createCriteria();
		Example exam = Example.create(entity);
		exam.excludeZeroes();
		String[] defPropertys = getSessionFactory().getClassMetadata(
				entityClass).getPropertyNames();
		for (String defProperty : defPropertys) {
			int ii = 0;
			for (ii = 0; ii < propertyNames.length; ++ii) {
				if (defProperty.equals(propertyNames[ii])) {
					criteria.addOrder(Order.asc(defProperty));
					break;
				}
			}
			if (ii == propertyNames.length) {
				exam.excludeProperty(defProperty);
			}
		}
		criteria.add(exam);
		return (List<T>) criteria.list();
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#find(java.lang.String, java.lang.Object[])
	 */
	public List find(String queryString, Object[] values) {
		return getHibernateTemplate().find(queryString, values);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#findByNamedParam(java.lang.String, java.lang.String[], java.lang.Object[])
	 */
	public List findByNamedParam(String queryString, String[] paramNames,
			Object[] values) {
		return getHibernateTemplate().findByNamedParam(queryString, paramNames,
				values);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#findByNamedQuery(java.lang.String)
	 */
	public List findByNamedQuery(String queryName) {
		return getHibernateTemplate().findByNamedQuery(queryName);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#findByNamedQuery(java.lang.String, java.lang.Object[])
	 */
	public List findByNamedQuery(String queryName, Object[] values) {
		return getHibernateTemplate().findByNamedQuery(queryName, values);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#findByNamedQueryAndNamedParam(java.lang.String, java.lang.String[], java.lang.Object[])
	 */
	public List<T> findByNamedQueryAndNamedParam(String queryName,
			String[] paramNames, Object[] values) {
		return getHibernateTemplate().findByNamedQueryAndNamedParam(queryName,
				paramNames, values);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#iterate(java.lang.String)
	 */
	public Iterator<T> iterate(String queryString) {
		return getHibernateTemplate().iterate(queryString);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#iterate(java.lang.String, java.lang.Object[])
	 */
	public Iterator<T> iterate(String queryString, Object[] values) {
		return getHibernateTemplate().iterate(queryString, values);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#closeIterator(java.util.Iterator)
	 */
	public void closeIterator(Iterator it) {
		getHibernateTemplate().closeIterator(it);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#createDetachedCriteria()
	 */
	public DetachedCriteria createDetachedCriteria() {
		return DetachedCriteria.forClass(this.entityClass);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#createCriteria()
	 */
	public Criteria createCriteria() {
		return this.createDetachedCriteria().getExecutableCriteria(
				this.getSession());
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#update(T)
	 */
	public void update(T entity) {
		getHibernateTemplate().update(entity);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#save(T)
	 */
	public void save(T entity) throws Exception {
		getSession().save(entity);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#saveOrUpdate(T)
	 */
	public void saveOrUpdate(T entity) {
		getHibernateTemplate().saveOrUpdate(entity);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#saveOrUpdateAll(java.util.Collection)
	 */
	public void saveOrUpdateAll(Collection<T> entities) {
		getHibernateTemplate().saveOrUpdateAll(entities);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#delete(T)
	 */
	public void delete(T entity) throws Exception {
		getHibernateTemplate().delete(entity);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#deleteByKey(PK)
	 */
	public void deleteByKey(PK id) throws Exception {
		if (this.getHibernateTemplate().get(entityClass,id)!=null) {
			this.delete(this.load(id));	
		}
	}

 

	/* (non-Javadoc)
	 * @see dao.GenericDAO#deleteAll(java.util.Collection)
	 */
	public void deleteAll(Collection<T> entities) {
		getHibernateTemplate().deleteAll(entities);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#load(PK)
	 */
	public T load(PK id) {
		return (T) getHibernateTemplate().load(entityClass, id);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#loadAll()
	 */
	public List<T> loadAll() {
		return (List<T>) getHibernateTemplate().loadAll(entityClass);
	}
	

	/* (non-Javadoc)
	 * @see dao.GenericDAO#getWithLock(PK, org.hibernate.LockMode)
	 */
	public T getWithLock(PK id, LockMode lock) {
		T t = (T) getHibernateTemplate().get(entityClass, id, lock);
		if (t != null) {
			this.flush(); // ����ˢ�£����������Ч��
		}
		return t;
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#loadWithLock(PK, org.hibernate.LockMode)
	 */
	public T loadWithLock(PK id, LockMode lock) {
		T t = (T) getHibernateTemplate().load(entityClass, id, lock);
		if (t != null) {
			this.flush(); // ����ˢ�£����������Ч��
		}
		return t;
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#updateWithLock(T, org.hibernate.LockMode)
	 */
	public void updateWithLock(T entity, LockMode lock) {
		getHibernateTemplate().update(entity, lock);
		this.flush(); // ����ˢ�£����������Ч��
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#deleteWithLock(T, org.hibernate.LockMode)
	 */
	public void deleteWithLock(T entity, LockMode lock) {
		getHibernateTemplate().delete(entity, lock);
		this.flush(); // ����ˢ�£����������Ч��
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#deleteByKeyWithLock(PK, org.hibernate.LockMode)
	 */
	public void deleteByKeyWithLock(PK id, LockMode lock) {
		this.deleteWithLock(this.load(id), lock);

	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#lock(T, org.hibernate.LockMode)
	 */
	public void lock(T entity, LockMode lock) {
		getHibernateTemplate().lock(entity, lock);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#initialize(java.lang.Object)
	 */
	public void initialize(Object proxy) {
		getHibernateTemplate().initialize(proxy);
	}

	/* (non-Javadoc)
	 * @see dao.GenericDAO#flush()
	 */
	public void flush() {
		getHibernateTemplate().flush();
	}

	public List<T> findBySqlToObj(final String SQL, final Class<T> cls) {
//		Session s = this.getSession();//s为Hibernate Session   
//		Query q = s.createQuery(hql);   
//		//1、将查询结果转换成List<Map<字段名,字段值>>   
//		q.setResultTransformer(org.hibernate.transform.Transformers.ALIAS_TO_ENTITY_MAP);   
//		//2、将结果转换成指定的bean   List<你的指定javaBean>   
//		q.setResultTransformer(org.hibernate.transform.Transformers.aliasToBean(Object.class));   
//		
		List list = this.getHibernateTemplate().executeFind(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException, SQLException {
						SQLQuery query = session.createSQLQuery(SQL);
						query.setResultTransformer(new BeanTransformer(cls));
						return query.list();
					}
				});
		return list;		
	}
	
	public List<T> findBySqlToObjForPage(final String SQL, final Class<T> cls, final Integer start, final Integer pageSize) {
		List list = this.getHibernateTemplate().executeFind(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException, SQLException {
						SQLQuery query = session.createSQLQuery(SQL);
						query.setFirstResult(start > 1 ? (start - 1) * pageSize : 0).setMaxResults(pageSize);
						query.setResultTransformer(new BeanTransformer(cls));
						return query.list();
					}
				});
		return list;
	}
	
	public Integer updateBySql(String sql) {
		Session session = getHibernateTemplate().getSessionFactory().openSession();   
	    Transaction tr = session.beginTransaction();  
	    Integer i = session.createSQLQuery(sql).executeUpdate();  
	    tr.commit();   
	    session.close();
	    return i;
	}

	@Override
	public Map findPage(int currPage, int pagesize, String hql) {
		int index=0;
		if((index =hql.indexOf("from "))>=0){
			String count = "select count(*) "+hql.substring(index);
			Long total=this.getRowCount(count);
			if(total>0){
				List list = this.findByHQL(currPage, pagesize, hql);
				Map map = new HashMap();
				map.put("total", total.intValue());
				map.put("list", list);
				return map;
			}
		}
		return null;
	}
	
	@Override
	public Map findCountAndPageBySql(int currPage,int pagesize, String sql,Class<T> cls) {
		int index=0;
		if((index =sql.indexOf("from "))>=0){
			int index1=sql.indexOf("order");
			String count="select count(*) "+sql.substring(index);
			if(index1>=0){
				count = "select count(*) "+sql.substring(index,index1);
			}
			Integer total=this.getRowCountBySql(count);
			if(total>0){
				List list = this.findBySqlToObjForPage(sql,cls,currPage, pagesize);
				Map map = new HashMap();
				map.put("total", total.intValue());
				map.put("list", list);
				return map;
			}
		}
		return null;
	}

	@Override
	public T merge(T entity) {
		return (T) getHibernateTemplate().merge(entity);

	}

}
